-module(pcapreader).
-include("protocol.hrl").
-export([start/2,handle_pcapfile/4,extract_int/4]).

start(Fn,Cb) ->
	spawn(pcapreader,handle_pcapfile,[Fn,open_file,Cb,{}]).
extract_int(little,0,Length,B) ->
	<<Res:Length/little-integer,_Other/binary>> = B,
	Res;
extract_int(little,Start,Length,B) ->
	<<_S:Start/integer,Res:Length/little-integer,_Other/binary>> = B,
	Res;
extract_int(big,0,Length,B) ->
	<<Res:Length/big-integer,_Other/binary>> = B,
	Res;
extract_int(big,Start,Length,B) ->
	<<_S:Start/integer,Res:Length/big-integer,_Other/binary>> = B,
	Res.
handle_pcapfile(Fn,open_file,Cb,{}) ->
	{ok,Fd} = file:open(Fn,[read,binary]),
	handle_pcapfile(Fd,bof,Cb,{});
handle_pcapfile(Fd,bof,Cb,{}) ->
	{ok,Magic} = file:pread(Fd,bof,4),
	{Endian,Type} =
		case Magic of
		<<16#a1,16#b2,16#C3,16#d4>> ->
			{big,pcap};
		<<16#d4,16#c3,16#b2,16#a1>> ->
			{little,pcap};
		<<16#0a,16#0d,16#0d,16#0a>> ->
			{little,pcapng};
		_ ->
			{big,unknown}
		end,
	handle_pcapfile(Fd,{bof,4},Cb,{header,{Endian,Type},[]});

handle_pcapfile(Fd,{bof,Loc},Cb,{header,{Endian,pcap},_}) ->
	{ok,Version} = 	file:pread(Fd,{bof,4},4),
	Major_V = extract_int(Endian,0,16,Version),
	Minor_V = extract_int(Endian,16,16,Version),
	Sta = Cb({verified,{Major_V,Minor_V},{}}),
	handle_pcapfile(Fd,{bof,8},Cb,{verified,{Major_V,Minor_V,Endian},Sta});

handle_pcapfile(Fd,{bof,Loc},Cb,{header,{_,pcapng},_}) ->
	handle_pcapngfile(Fd,{bof,Loc},Cb,{});

handle_pcapfile(Fd,{bof,Loc},Cb,{verified,{Major_V,Minor_V,Endian},Sta}) ->
	{ok,<<Timer_Set:64,SandNI:64/bitstring>>} = file:pread(Fd,{bof,Loc},16),
	Snap_Length = extract_int(Endian,0,32,SandNI),
	NI_Type = extract_int(Endian,32,32,SandNI),
	NewSta = Cb({parsed_header,{Timer_Set,Snap_Length,NI_Type},Sta}),
	handle_pcapfile(Fd,{bof,Loc + 16},Cb,{analysis_in,{Major_V,Minor_V,Endian},Snap_Length,0,NewSta});

handle_pcapfile(Fd,{bof,Loc},Cb,{analysis_in,{Major_V,Minor_V,Endian},S_Packet_Counter,Counter,Sta}) ->
	case file:pread(Fd,{bof,Loc},16) of
	{ok,<<Capture_Time:64/bitstring,S_D_Len:64/bitstring>>} ->
		S_P_Len = extract_int(Endian,0,32,S_D_Len),
		{ok,Packet} = file:pread(Fd,{bof,Loc + 16},S_P_Len),
		NewSta = Cb({packet_comming,
			#packet_tuple{packet = Packet,size = S_P_Len,context = [#l1_protocol{index=Counter, coming_datetime= {Endian,Capture_Time},interface_id=1}],datetime={Endian,Capture_Time}},
			Sta}),
		handle_pcapfile(Fd,{bof,Loc + 16 + S_P_Len},Cb,{analysis_in,{Major_V,Minor_V,Endian},S_Packet_Counter,Counter + 1,NewSta});
	eof ->
		Cb({eof,Loc,Sta});
	Other ->
		io:format("unexcepted case switch Loc:~p read:~p~n",[Loc,Other]),
		ok
	end.

handle_pcapngfile(Fd,{bof,Loc},Cb,{}) ->
	{ok,<<BlockLength:32/little-integer>>} = 	file:pread(Fd,{bof,Loc},4),
	{ok,Magic} = file:pread(Fd,{bof,Loc + 4},4),
	Endian =
		case Magic of
		<<16#1a,16#2b,16#3c,16#4d>> ->
			big;
		<<16#4d,16#3c,16#2b,16#1a>> ->
			little;
		_ ->
			big
		end,
	{ok,Version} = 	file:pread(Fd,{bof,Loc + 4 + 4},4),
	Major_V = extract_int(Endian,0,16,Version),
	Minor_V = extract_int(Endian,16,16,Version),
	io:format("ng pcap ~p version ~p ~pq",[BlockLength,Major_V,Minor_V]),
	Sta = Cb({verified,{Major_V,Minor_V},{}}).



